package winapi

import (
	"fmt"
	"syscall"
	"unsafe"

	"github.com/lxn/win"
	"golang.org/x/sys/windows"
)

var (
	// Library
	libkernel32 *windows.LazyDLL

	// Functions
	virtualAllocEx    *windows.LazyProc
	virtualFreeEx     *windows.LazyProc
	readProcessMemory *windows.LazyProc
)

// define
const (
	PROCESS_ALL_ACCESS = 0x1f0fff
	MEM_COMMIT         = 0x00001000
	MEM_RELEASE        = 0x8000
	PAGE_READWRITE     = 0x4
)

func init() {
	// Library
	libkernel32 = windows.NewLazySystemDLL("kernel32.dll")

	// Functions
	virtualAllocEx = libkernel32.NewProc("VirtualAllocEx")
	virtualFreeEx = libkernel32.NewProc("VirtualFreeEx")
	readProcessMemory = libkernel32.NewProc("ReadProcessMemory")

}

func VirtualAllocEx(hProcess syscall.Handle, dwSize int32) (uint32, error) {
	ret, _, _ := syscall.Syscall6(virtualAllocEx.Addr(), 5,
		uintptr(hProcess),
		uintptr(0),
		uintptr(dwSize),
		uintptr(MEM_COMMIT),
		uintptr(PAGE_READWRITE),
		uintptr(0),
	)

	if ret == 0 {
		return 0, fmt.Errorf("error code: %x", win.GetLastError())

	}
	return uint32(ret), nil
}

func VirtualFreeEx(hProcess syscall.Handle, lpAddress uint32) error {
	ret, _, _ := syscall.Syscall6(virtualFreeEx.Addr(), 4,
		uintptr(hProcess),
		uintptr(lpAddress),
		uintptr(0),
		uintptr(MEM_RELEASE),
		uintptr(0),
		uintptr(0),
	)

	if ret == 0 {
		return fmt.Errorf("error code: %x", win.GetLastError())
	}

	return nil
}

func ReadProcessMemory(hProcess syscall.Handle, lpBaseAddress uint32, size int32) ([]byte, error) {
	buf := make([]byte, size)

	ret, _, _ := syscall.Syscall6(readProcessMemory.Addr(), 5,
		uintptr(hProcess),
		uintptr(lpBaseAddress),
		uintptr(unsafe.Pointer(&buf[0])),
		uintptr(size),
		uintptr(0),
		uintptr(0),
	)
	if ret == 0 {
		return nil, fmt.Errorf("error code: %x", win.GetLastError())
	}

	return buf, nil
}
